<template><template><template>

  <div class="search-result-list-container">

    <van-row class="search0-body">  <div class="search-result-list-container">	<div class="form-drop-list">

      <van-col :span="22">

        <van-field    <van-row class="search0-body">		<van-field

          v-model="displayText"

          :label="label"      <van-col :span="22">			v-model="searchText"

          :required="required"

          :readonly="readonly"        <van-field			:label="label"

          input-align="left"

          :placeholder="placeholder"          v-model="displayText"			:required="required"

          clearable

          @input="handleInputSearch"          :label="label"			:readonly="readonly"

        />

      </van-col>          :required="required"			:disabled="disabled"

      <van-col :span="2">

        <van-icon name="search" class="list-container-search" />          :readonly="readonly"			:placeholder="placeholder"

      </van-col>

    </van-row>          input-align="left"			clearable

    

    <van-row v-if="showList" class="list-container">          :placeholder="placeholder"			@focus="onFocus"

      <van-col :span="24">

        <van-cell          clearable			@blur="onBlur"

          v-for="(item, index) in itemData"

          :key="index"          @input="handleInputSearch"			@input="onInput"

          :title="item[showText]"

          class="value-class"        />		/>

          clickable

          @click="toggle(item, index)"      </van-col>		<div v-if="showDropdown" class="dropdown-list">

        />

      </van-col>      <van-col :span="2">			<div class="select-item" v-for="item in filteredData" :key="item[chooseValue]" @click="confirm(item)">

    </van-row>

            <van-icon name="search" class="list-container-search" />				<div class="text">{{ item[showText] }}</div>

    <van-row v-if="itemData.length === 0 && showList" class="list-container">

      <van-col :span="24">      </van-col>				<van-icon v-if="modelValue === item[chooseValue]" name="success" color="red" />

        <div class="to-add-data" @click="toAdd">

          搜不到企业？去添加企业    </van-row>			</div>

        </div>

      </van-col>    			<div v-if="filteredData.length === 0" class="to-add-data" @click="toAdd">搜不到企业？去添加企业</div>

    </van-row>

  </div>    <van-row v-if="showList" class="list-container">		</div>

</template>

      <van-col :span="24">	</div>

<script>

import { ref, computed, watch, onMounted, nextTick } from 'vue'        <van-cell</template>

import { useRouter } from 'vue-router'

import { Toast } from 'vant'          v-for="(item, index) in itemData"

import { post } from '@/api/request'

          :key="index"<script>

const debounce = (func, delay = 300) => {

  let timeoutId          :title="item[showText]"import { ref, watch, onMounted, getCurrentInstance } from 'vue'

  return (...args) => {

    clearTimeout(timeoutId)          class="value-class"import { showToast } from 'vant'

    timeoutId = setTimeout(() => func.apply(null, args), delay)

  }          clickableimport { post } from '../../api'

}

          @click="toggle(item, index)"export default {

export default {

  name: 'FormDropList',        />	name: 'FormDropList',

  props: {

    modelValue: { type: [String, Number], default: '' },      </van-col>	props: {

    label: { type: String, default: '' },

    required: { type: Boolean, default: false },    </van-row>		modelValue: { type: [String, Number], default: '' },

    readonly: { type: Boolean, default: false },

    key: { type: String, default: '' },    		itemKey: { type: String, default: '' },

    defaultText: { type: String, default: '' },

    showText: { type: String, default: 'name' },    <van-row v-if="itemData.length === 0 && showList" class="list-container">		label: { type: String, default: '' },

    chooseValue: { type: String, default: 'id' },

    httpUrl: { type: String, default: '' },      <van-col :span="24">		required: { type: Boolean, default: false },

    httpParams: { type: Object, default: () => ({}) },

    placeholder: { type: String, default: '请输入搜索' },        <div class="to-add-data" @click="toAdd">		disabled: { type: Boolean, default: false },

  },

  emits: ['update:modelValue', 'onListSelectedChange'],          搜不到企业？去添加企业		readonly: { type: Boolean, default: false },

  setup(props, { emit }) {

    const router = useRouter()        </div>		showText: { type: String, default: 'text' },

    

    const itemData = ref([])      </van-col>		chooseValue: { type: String, default: 'value' },

    const itemDataAll = ref([])

    const dataSelectedInfo = ref({})    </van-row>		httpUrl: { type: String, default: '' },

    const showList = ref(false)

    const searchText = ref('')  </div>		httpParams: { type: Object, default: () => ({}) },

    

    const displayText = computed({</template>		placeholder: { type: String, default: '请选择' },

      get() {

        return dataSelectedInfo.value[props.showText] || props.defaultText || searchText.value	},

      },

      set(val) {<script>	emits: ['update:modelValue', 'selected', 'change'],

        searchText.value = val

      }import { ref, computed, watch, onMounted, nextTick } from 'vue'	setup(props, { emit }) {

    })

import { useRouter } from 'vue-router'		const instance = getCurrentInstance()

    const queryData = async (params = {}) => {

      if (!props.httpUrl) return []import { Toast } from 'vant'		const showDropdown = ref(false)

      

      try {import { post } from '@/api/request'		const itemData = ref([])

        itemData.value = []

        		const searchText = ref('')

        const requestParams = { ...props.httpParams, ...params }

        const res = await post(props.httpUrl, requestParams)// 防抖函数工具		const filteredData = ref([])

        

        if (res.code === '0' && Array.isArray(res.data)) {const debounce = (func, delay = 300) => {		const isTyping = ref(false)

          itemData.value = res.data

            let timeoutId

          if (itemDataAll.value.length === 0 || props.httpParams.reLoadData) {

            itemDataAll.value = res.data  return (...args) => {		const fetchList = async () => {

          }

              clearTimeout(timeoutId)			if (!props.httpUrl) return

          await nextTick()

          initData()    timeoutId = setTimeout(() => func.apply(null, args), delay)			try {

          

          return res.data  }				const params = { ...props.httpParams }

        } else {

          throw new Error(res.msg || '查询失败')}				const res = await post(props.httpUrl, params)

        }

      } catch (error) {				if (res && Array.isArray(res.data)) {

        console.error('查询数据失败:', error)

        Toast({ type: 'fail', message: '获取列表失败' })export default {					itemData.value = res.data

        itemData.value = []

        return []  name: 'FormDropList',					filteredData.value = res.data

      }

    }  props: {				} else {



    const debouncedQuery = debounce((searchParams) => {    modelValue: { type: [String, Number], default: '' },					itemData.value = []

      queryData(searchParams)

    }, 300)    label: { type: String, default: '' },					filteredData.value = []



    const initData = () => {    required: { type: Boolean, default: false },				}

      setTimeout(() => {

        if (props.modelValue && itemDataAll.value.length > 0) {    readonly: { type: Boolean, default: false },			} catch (e) {

          const selectedItem = itemDataAll.value.find(item => 

            item[props.chooseValue] === props.modelValue    key: { type: String, default: '' },				showToast('获取列表失败')

          )

              defaultText: { type: String, default: '' },				itemData.value = []

          if (selectedItem) {

            dataSelectedInfo.value = selectedItem    showText: { type: String, default: 'name' },				filteredData.value = []

            emit('onListSelectedChange', {

              key: props.key,    chooseValue: { type: String, default: 'id' },			}

              value: props.modelValue,

              dataSelectedInfo: selectedItem    httpUrl: { type: String, default: '' },		}

            })

          }    httpParams: { type: Object, default: () => ({}) },

        }

      }, 200)    placeholder: { type: String, default: '请输入搜索' },		const filterData = (query) => {

    }

  },			if (!query || typeof query !== 'string') {

    const handleInputSearch = (value) => {

      console.log('搜索输入:', value)  emits: ['update:modelValue', 'onListSelectedChange'],				filteredData.value = itemData.value

      searchText.value = value

      showList.value = true  setup(props, { emit }) {			} else {

      

      debouncedQuery({ searchText: value })    const router = useRouter()				const queryLower = query.toLowerCase()

    }

    				filteredData.value = itemData.value.filter((item) => {

    const toggle = (item, index) => {

      console.log('选择项目:', item)    // 响应式数据					const itemText = item[props.showText]

      

      const selectedValue = item[props.chooseValue]    const itemData = ref([])					return itemText && typeof itemText === 'string' && itemText.toLowerCase().includes(queryLower)

      

      dataSelectedInfo.value = item    const itemDataAll = ref([])				})

      searchText.value = ''

      showList.value = false    const dataSelectedInfo = ref({})			}

      

      emit('update:modelValue', selectedValue)    const showList = ref(false)		}

      emit('onListSelectedChange', {

        key: props.key,    const searchText = ref('')

        value: selectedValue,

        dataSelectedInfo: item    		const syncText = () => {

      })

    }    // 显示文本的计算属性 - 完全模拟小程序逻辑			// 如果用户正在输入，不要覆盖用户的输入



    const toAdd = () => {    const displayText = computed({			if (isTyping.value) return

      console.log('跳转到添加页面')

      try {      get() {

        router.push('/company/company-add')

      } catch (error) {        return dataSelectedInfo.value[props.showText] || props.defaultText || searchText.value			const target = itemData.value.find((o) => o[props.chooseValue] === props.modelValue)

        window.location.href = '#/company/company-add'

      }      },			const newText = target ? target[props.showText] : ''

    }

      set(val) {

    watch(() => props.modelValue, (newValue, oldValue) => {

      if (newValue !== oldValue && newValue && itemDataAll.value.length > 0) {        searchText.value = val			// 只有当文本真正不同时才更新，避免循环触发

        initData()

      }      }			if (searchText.value !== newText) {

    }, { immediate: true })

    })				console.log('syncText setting:', newText)

    watch(() => props.httpParams, (newParams, oldParams) => {

      if (JSON.stringify(newParams) !== JSON.stringify(oldParams)) {				searchText.value = newText

        queryData()

      }    // 数据查询函数 - 完全按照小程序逻辑			}

    }, { deep: true, immediate: false })

    const queryData = async (params = {}) => {		}

    onMounted(() => {

      queryData()      if (!props.httpUrl) return []

    })

      		watch(() => props.modelValue, syncText)

    return {

      itemData,      try {		watch(itemData, () => {

      itemDataAll,

      dataSelectedInfo,        // 清空当前数据			syncText()

      showList,

      displayText,        itemData.value = []			filterData(searchText.value)

      searchText,

      handleInputSearch,        		})

      toggle,

      toAdd        const requestParams = { ...props.httpParams, ...params }

    }

  }        const res = await post(props.httpUrl, requestParams)		// 监听搜索文本变化，替代@input事件

}

</script>        		watch(



<style lang="less" scoped>        if (res.code === '0' && Array.isArray(res.data)) {			searchText,

.search-result-list-container {

  position: relative;          itemData.value = res.data			(newValue, oldValue) => {

}

          				console.log('searchText changed:', newValue, 'oldValue:', oldValue, 'isTyping:', isTyping.value)

.value-class {

  flex: none !important;          // 第一次加载或需要重新加载时更新全量数据				// 总是进行过滤，无论是用户输入还是程序设置

}

          if (itemDataAll.value.length === 0 || props.httpParams.reLoadData) {				filterData(newValue)

.list-container {

  position: absolute;            itemDataAll.value = res.data				// 如果有内容且用户正在输入，显示下拉列表

  z-index: 100;

  background: #fff;          }				if (isTyping.value && newValue !== '') {

  width: 105%;

  text-align: left;          					showDropdown.value = true

  top: 50px;

  height: 400px;          // 异步初始化选中数据				}

  overflow: auto;

}          await nextTick()			},



.list-container-search {          initData()			{ immediate: false }

  position: absolute;

  right: 20px;          		)

  top: 15px;

}          return res.data



.check-hide {        } else {		const onInput = (value) => {

  display: none;

}          throw new Error(res.msg || '查询失败')			console.log('onInput triggered:', value)



.check-active {        }			// 用户开始输入，标记为正在输入

  display: block;

}      } catch (error) {			isTyping.value = true



.to-add-data {        console.error('查询数据失败:', error)			// 注意：这里不直接设置 searchText.value，因为 v-model 会自动处理

  text-align: center;

  color: rgba(78, 142, 236, 0.867);        Toast({ type: 'fail', message: '获取列表失败' })		}

  padding: 12px;

  cursor: pointer;        itemData.value = []

}

        return []		const onFocus = () => {

.to-add-data:hover {

  background-color: #f7f8fa;      }			if (props.disabled || props.readonly) return

}

</style>    }			if (itemData.value.length === 0) {

				fetchList()

    // 防抖查询 - 模拟小程序的 debounceFunc			}

    const debouncedQuery = debounce((searchParams) => {			showDropdown.value = true

      queryData(searchParams)		}

    }, 300)

		const onBlur = () => {

    // 初始化选中数据 - 完全按照小程序逻辑			// 延迟隐藏，让点击事件先触发

    const initData = () => {			setTimeout(() => {

      setTimeout(() => {				showDropdown.value = false

        if (props.modelValue && itemDataAll.value.length > 0) {				isTyping.value = false

          const selectedItem = itemDataAll.value.find(item => 			}, 200)

            item[props.chooseValue] === props.modelValue		}

          )

          		const confirm = (item) => {

          if (selectedItem) {			isTyping.value = false

            dataSelectedInfo.value = selectedItem			emit('update:modelValue', item[props.chooseValue])

            // 触发选择变更事件 - 保持与小程序相同的事件结构			emit('selected', { data: item })

            emit('onListSelectedChange', {			emit('change', { [props.itemKey]: item[props.chooseValue] })

              key: props.key,			searchText.value = item[props.showText]

              value: props.modelValue,			showDropdown.value = false

              dataSelectedInfo: selectedItem		}

            })

          }		const toAdd = () => {

        }			// 跳转到企业添加页面

      }, 200)			const router = instance?.appContext.config.globalProperties.$router

    }			if (router) {

				router.push('/company/company-add')

    // 输入搜索处理 - 完全模拟小程序逻辑			} else {

    const handleInputSearch = (value) => {				// 如果没有路由，使用window.location

      console.log('搜索输入:', value)				window.location.href = '#/company/company-add'

      searchText.value = value			}

      showList.value = true			showDropdown.value = false

      		}

      // 防抖搜索，传递搜索文本参数

      debouncedQuery({ searchText: value })		onMounted(() => {

    }			fetchList()

			syncText()

    // 选择项目 - 完全按照小程序的 toggle 逻辑		})

    const toggle = (item, index) => {

      console.log('选择项目:', item)		return { showDropdown, itemData, searchText, filteredData, onInput, onFocus, onBlur, confirm, toAdd }

      	},

      const selectedValue = item[props.chooseValue]}

      </script>

      // 更新选中状态

      dataSelectedInfo.value = item<style lang="less" scoped>

      searchText.value = ''.form-drop-list {

      showList.value = false	position: relative;

      }

      // 发出事件 - 保持与小程序相同的事件结构

      emit('update:modelValue', selectedValue).dropdown-list {

      emit('onListSelectedChange', {	position: absolute;

        key: props.key,	top: 100%;

        value: selectedValue,	left: 0;

        dataSelectedInfo: item	right: 0;

      })	z-index: 1000;

    }	background: #fff;

	border: 1px solid #ebedf0;

    // 跳转到添加页面 - 完全模拟小程序逻辑	border-top: none;

    const toAdd = () => {	border-radius: 0 0 4px 4px;

      console.log('跳转到添加页面')	max-height: 300px;

      try {	overflow-y: auto;

        router.push('/company/company-add')	box-shadow: 0 2px 12px rgba(100, 101, 102, 0.12);

      } catch (error) {}

        // 降级处理

        window.location.href = '#/company/company-add'.select-item {

      }	display: flex;

    }	align-items: center;

	padding: 12px 16px;

    // 监听 modelValue 变化 - 模拟小程序的 observer	cursor: pointer;

    watch(() => props.modelValue, (newValue, oldValue) => {	border-bottom: 1px solid #f7f8fa;

      if (newValue !== oldValue && newValue && itemDataAll.value.length > 0) {

        initData()	&:hover {

      }		background-color: #f7f8fa;

    }, { immediate: true })	}



    // 监听 httpParams 变化 - 完全模拟小程序的 observer	&:last-child {

    watch(() => props.httpParams, (newParams, oldParams) => {		border-bottom: none;

      if (JSON.stringify(newParams) !== JSON.stringify(oldParams)) {	}

        queryData()}

      }

    }, { deep: true, immediate: false }).text {

	flex: 1;

    // 组件挂载时初始化 - 模拟小程序的 ready	font-size: 14px;

    onMounted(() => {	color: #323233;

      queryData()}

    })

.to-add-data {

    return {	text-align: center;

      itemData,	color: rgba(78, 142, 236, 0.867);

      itemDataAll,	padding: 16px 8px;

      dataSelectedInfo,	cursor: pointer;

      showList,	border-top: 1px solid #f7f8fa;

      displayText,

      searchText,	&:hover {

      handleInputSearch,		background-color: #f7f8fa;

      toggle,	}

      toAdd}

    }</style>

  }
}
</script>

<style lang="less" scoped>
/* 完全复制小程序样式 */
.search-result-list-container {
  position: relative;
}

.value-class {
  flex: none !important;
}

.list-container {
  position: absolute;
  z-index: 100;
  background: #fff;
  width: 105%;
  text-align: left;
  top: 50px;
  height: 400px;
  overflow: auto;
}

.list-container-search {
  position: absolute;
  right: 20px;
  top: 15px;
}

.check-hide {
  display: none;
}

.check-active {
  display: block;
}

.to-add-data {
  text-align: center;
  color: rgba(78, 142, 236, 0.867);
  padding: 12px;
  cursor: pointer;
}

.to-add-data:hover {
  background-color: #f7f8fa;
}
</style>